#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "spinlock.h"

// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256];
extern uint vectors[]; // in vectors.S: array of 256 entry pointers
struct spinlock tickslock;
uint ticks;

void tvinit(void)
{
  int i;

  for (i = 0; i < 256; i++)
    SETGATE(idt[i], 0, SEG_KCODE << 3, vectors[i], 0);
  SETGATE(idt[T_SYSCALL], 1, SEG_KCODE << 3, vectors[T_SYSCALL], DPL_USER);

  initlock(&tickslock, "time");
}

void idtinit(void)
{
  lidt(idt, sizeof(idt));
}

int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);
// PAGEBREAK: 41
void trap(struct trapframe *tf)
{
  if (tf->trapno == T_SYSCALL)
  {
    if (myproc()->killed)
      exit();
    myproc()->tf = tf;
    syscall();
    if (myproc()->killed)
      exit();
    return;
  }

  switch (tf->trapno)
  {
  case T_IRQ0 + IRQ_TIMER:
    if (cpuid() == 0)
    {
      acquire(&tickslock);
      ticks++;
      wakeup(&ticks);
      release(&tickslock);
    }

    // 实验三添加
    if (myproc() && (tf->cs & 3) == 3)
    {
      myproc()->curalarmticks++;
      if (myproc()->alarmticks == myproc()->curalarmticks)
      { // 如果已经到达了周期
        myproc()->curalarmticks = 0;

        // 进行压栈处理
        tf->esp -= 4;
        *((uint *)(tf->esp)) = tf->eip;
        // 准备进行函数执行
        tf->eip = (uint)myproc()->alarmhandler;
      }
    }
    // 结尾

    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE:
    ideintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE + 1:
    // Bochs generates spurious IDE1 interrupts.
    break;
  case T_IRQ0 + IRQ_KBD:
    kbdintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_COM1:
    uartintr();
    lapiceoi();
    break;
  case T_IRQ0 + 7:
  case T_IRQ0 + IRQ_SPURIOUS:
    cprintf("cpu%d: spurious interrupt at %x:%x\n",
            cpuid(), tf->cs, tf->eip);
    lapiceoi();
    break;

  // PAGEBREAK: 13
  default:
    if (myproc() == 0 || (tf->cs & 3) == 0)
    {
      // In kernel, it must be our mistake.
      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
              tf->trapno, cpuid(), tf->eip, rcr2());
      panic("trap");
    }

    // 实验五添加(第二阶段)
    char *mem = kalloc();
    // 获取触发缺页异常的虚拟地址，并向下对齐到页面边界（4KB对齐）
    uint a = PGROUNDDOWN(rcr2());
    // 检查物理内存是否分配成功
    if (mem != 0)
    {
      // 将新分配的物理页清零（防止数据泄漏）
      memset(mem, 0, PGSIZE);
      // 再次检查内存是否有效（防御性编程，但通常不会触发）
      if (mem == 0)
      {
        // 打印内存不足错误信息
        cprintf("allocuvm out of memory\n");
        // 回滚进程地址空间增长（恢复sz）
        deallocuvm(myproc()->pgdir, myproc()->sz, myproc()->tf->eax);
        return;
      }
      // 声明mappages函数（因原函数在vm.c中是static，需手动声明）
      extern int mappages(pde_t * pgdir, void *a, uint size, uint pa, int perm);

      // 将物理页映射到用户页表中：
      if (mappages(myproc()->pgdir, (void *)a, PGSIZE, V2P(mem), PTE_W | PTE_U) >= 0)
      {
        // 映射成功，跳出异常处理流程，恢复用户进程执行
        break;
      }
      else
      {
        // 映射失败（如页表项已占用）
        cprintf("allocuvm out of memory (2)\n");
        // 回滚地址空间增长
        deallocuvm(myproc()->pgdir, myproc()->sz, myproc()->tf->eax);
        // 释放之前分配的物理页
        kfree(mem);
        return;
      }
    }
    // 结尾

    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %d err %d on cpu %d "
            "eip 0x%x addr 0x%x--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno,
            tf->err, cpuid(), tf->eip, rcr2());
    myproc()->killed = 1;
  }

  // Force process exit if it has been killed and is in user space.
  // (If it is still executing in the kernel, let it keep running
  // until it gets to the regular system call return.)
  if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER)
    exit();

  // Force process to give up CPU on clock tick.
  // If interrupts were on while locks held, would need to check nlock.
  if (myproc() && myproc()->state == RUNNING &&
      tf->trapno == T_IRQ0 + IRQ_TIMER)
    yield();

  // Check if the process has been killed since we yielded
  if (myproc() && myproc()->killed && (tf->cs & 3) == DPL_USER)
    exit();
}